
#include <vector>
#include <algorithm>
#include <queue>
#include <iostream>
#include <stdlib.h>
#include <strings.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include "EfficientMap.cc"
#include "TwoWayList.cc"
#include "Types.cc"
#include "BlockSortedTempTable.h"

class AggRecord;
class InRecord;

class InRecord
{

private:

#include "Atts.cc"

  // Example of what would be in Atts.cc
  /*
    Int n_n_nationkey;
    Str n_n_name;
    Int n_n_regionkey;
    Str n_n_comment;
  */

public:

  InRecord  () {}
  ~InRecord  () {}

  bool ReadIn (FILE *fromMe)
  {
    bool res;

#include "ReadIn.cc"

    // Example of what would be in ReadIn.cc
    /*
      res = n_n_nationkey.ReadIn(fromMe);
      res = n_n_name.ReadIn(fromMe);
      res = n_n_regionkey.ReadIn(fromMe);
      res = n_n_comment.ReadIn(fromMe);
    */

    fgetc_unlocked(fromMe);

    return res;
  }

  void WriteBuffer(FILE *toMe)
  {
	
#include "WriteBuffer.cc"

    // Example of what would be in WriteBuffer.cc
    /*
      n_n_nationkey.WriteOut(toMe);
      n_n_name.WriteOut(toMe);
      n_n_regionkey.WriteOut(toMe);
      n_n_comment.WriteOut(toMe);
    */

    fputc_unlocked('\n', toMe);
  }

  // It looks like SortFunctions.cc is not needed for group by execution since
  // group by execution just uses simple functions to compare. Arguably I should
  // be able to do the same thing to simplify my order by code.

  //#include "SortFunctions.cc"

  // Example of what would be included in SortFunctions.cc
  /*
    Int sortFunction1() const { return n_n_nationkey; }
    Str sortFunction2() const { return n_n_name; }
    Int sortFunction3() const { return n_n_regionkey; }
  */

  bool operator < (const InRecord & r2) const
  {

#include "Compare.cc"

    // example of what would be in Compare.cc
    /*
      if (sortFunction1() > r2.sortFunction1()) return true;
      if (sortFunction1() < r2.sortFunction1()) return false;
      if (sortFunction2() < r2.sortFunction2()) return true;
      if (sortFunction2() > r2.sortFunction2()) return false;
      if (sortFunction3() < r2.sortFunction3()) return true;
      if (sortFunction3() > r2.sortFunction3()) return false;
    */

    return false;
  }

  // Aggregates this record to the AggRecord
  // Also initializes the aggregate record for the first time.
  void aggregate(AggRecord & aggMe) const;
};

class AggRecord
{
    
private:

#include "AggAtts.cc"

  // example of what would be in AggAtts.cc
  /*
  // Grouping Attributes
  Int ccccccc_n_n_regionkey;

  // Output Attributes
  Int att0; // corresponds to ccccccc_n_n_regionkey
  Int att1; // corresponds to sum aggregate function
  Int att2; // corresponds to avg aggregate function

  // Track Count Each Time - Technically this is only needed for
  // computing averages so maybe it would be a slight optimization
  // to remove this
  Int ccccccc_count;
  */

  friend class InRecord;

public:

  // Not needed in two pass algorithm
  /*
    bool checkSameGroup(AggRecord o)
    {

    //#include "CheckSameGroup.cc"

    // example of what would be in CheckSameGroup.cc
    return true && (ccccccc_n_n_regionkey == o.ccccccc_n_n_regionkey);
    }
  */

  void WriteOut(FILE *toMe)
  {

#include "WriteOut.cc"
	    
    // example of what would be WriteOut.cc
    /*
      att0.WriteOut(toMe);
      att1.WriteOut(toMe);
      (att2 / ccccccc_count).WriteOut(toMe);
    */

    fputc_unlocked('\n', toMe);
  }

  AggRecord() {}
  ~AggRecord() {}
};

void InRecord::aggregate(AggRecord & aggMe) const
{

#include "DoAgg.cc"

    // example of what would be in DoAgg.cc
    /*
      aggMe.ccccccc_n_n_regionkey = n_n_regionkey;
      aggMe.att0 = n_n_regionkey;
      aggMe.att1 = aggMe.att1 + Int(1);
      aggMe.att2 = aggMe.att2 + Int(1);
      aggMe.ccccccc_count = aggMe.ccccccc_count + Int(1);
    */
}

int
main(int argc, char **argv)
{
  if (argc != 3)
    {
      fprintf(stderr, "Usage: TPGroupBy inFile outFile");
      exit(-1);
    }

  char *inFile = argv[1];
  char *outFile = argv[2];

  BlockSortedTempTable<InRecord> *bstt = new BlockSortedTempTable<InRecord>(inFile);
  bstt->writeBlocks();
  bstt->initForRead();

  FILE *f = fopen(outFile, "w");

  InRecord currentGroupingRecord;

  if (!bstt->next(currentGroupingRecord))
    {
      bstt->cleanup();
      return 0;
    }

  printf("DEBUG: Opened bstt for reading.\n");

  // TODO: Write a proper copy constructor so this doesn't
  // need to be a pointer.
  AggRecord *agg = new AggRecord();
  currentGroupingRecord.aggregate(*agg);

  InRecord nextRecord;
  while (bstt->next(nextRecord))
    {
      if (currentGroupingRecord < nextRecord)
	{
	  agg->WriteOut(f);
	  currentGroupingRecord = nextRecord;
	  delete agg;
	  agg = new AggRecord();
	}
      nextRecord.aggregate(*agg);
    }

  agg->WriteOut(f);
  delete agg;

  bstt->cleanup();

  return 0;
}

